unit uLEDCalc;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, uBaseCalcForm, ExtCtrls, StdCtrls, Buttons, pxml;

type
  TfmLEDCalc = class(TfmBaseCalcForm)
    Panel1: TPanel;
    imgMain: TImage;
    edP1: TLabeledEdit;
    laP1: TLabel;
    edP2: TLabeledEdit;
    laP2: TLabel;
    edP3: TLabeledEdit;
    laP3: TLabel;
    edResult: TLabeledEdit;
    imgRes: TImage;
    Bevel1: TBevel;
    edP4: TLabeledEdit;
    laP4: TLabel;
    Label1: TLabel;
    cbLEDBase: TComboBox;
    Label2: TLabel;
    sbAdd: TSpeedButton;
    sbDelete: TSpeedButton;
    Shape1: TShape;
    edLedVolt: TLabeledEdit;
    laLedVolt: TLabel;
    cbVccSource: TComboBox;
    laVccSource: TLabel;
    laRcap: TLabel;
    laR2: TLabel;
    procedure DoFieldChange(Sender: TObject);
    procedure imgResClick(Sender: TObject);
    procedure cbLEDBaseChange(Sender: TObject);
    procedure sbAddClick(Sender: TObject);
    procedure sbDeleteClick(Sender: TObject);
    procedure edP3Change(Sender: TObject);
    procedure cbVccSourceChange(Sender: TObject);
  private
    { Private declarations }
    FValues: array[0..5] of single;
    FBase: TPXMLDocument;
    procedure ReloadList;
    procedure CalcDCRes;
    procedure CalcACCap;
  protected
    function DoCheckFields: boolean; override;
    procedure FieldsChanged; override;
  public
    { Public declarations }
    class function CalcFriendlyName: string; override;
    class function RunKey: string; override;
    class procedure InvokeCalc; override;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

var
  fmLEDCalc: TfmLEDCalc;

implementation

{$R *.dfm}

uses uFormReg, uENoms, strUtils, uLocalization;

function readFloatAttr(node: TPXMLNode; attr: wideString): single;
var tmp: string;
begin
  tmp := node.getAttribute(attr);
  tmp := AnsiReplaceStr(tmp,'.',DecimalSeparator);
  Result := strToFloatDef(tmp, 0);
end;

{ TfmLEDCalc }

procedure TfmLEDCalc.AfterConstruction;
begin
  inherited;
  LoadPictureFromRes(imgRes.Picture, 'WARN');

  FBase := TPXMLDocument.Create;
  if FileExists(AppDir+'Data\led.xml') then FBase.Load(AppDir+'Data\led.xml')
  else FBase.LoadXML('<?xml version="1.0" encoding="UTF-8" ?><base />');

  ReloadList;

  Label1.Caption := getLocaleString('seltitle');
  Label2.Caption := getLocaleString('seldesc');
  SetLabels('LedVolt', getLocaleString('ledvolt'), getString('#global\volt'));
  SetLabels('P1', getString('#global\vcc'), getString('#global\volt'));
  SetLabels('P2', getLocaleString('vfwd'), getString('#global\volt'));
  SetLabels('P3', getLocaleString('ifwd'), getString('#global\ma'));
  SetLabels('P4', getLocaleString('ledcnt'), getString('#global\pcs'));
  laVccSource.Caption := GetLocaleString('vccsource');
  LocalizeStrings(cbVccSource.Items);

  cbVccSource.ItemIndex := 0;
  cbVccSourceChange(cbVccSource);

  laRCap.Caption := FormatValue(7.5e5);
  laR2.Caption := FormatValue(10);
end;

function TfmLEDCalc.DoCheckFields: boolean;
var tmp: single;
begin
  Result := false;
  sbAdd.Enabled := false;

  edLedVolt.Text := Format('%.2f', [StrToFloatDef(edP2.Text,0)*StrToFloatDef(edP4.Text,0)]);

  tmp := CheckField(edP2);
  if tmp<=0 then exit;

  tmp := CheckField(edP4);
  if tmp<=0 then exit;

  sbAdd.Enabled := true;

  if CheckField(edP1)<=0 then exit;

  tmp := CheckField(edP3);
  if tmp<=0 then exit;

  Result := true;
end;

procedure TfmLEDCalc.FieldsChanged;
begin
  inherited;
  edResult.Text := getString('#global\invaliddata');

  FValues[1] := StrToFloat(edP2.Text);
  FValues[2] := StrToFloat(edP3.Text);
  FValues[3] := StrToFloat(edP4.Text);

  case cbVccSource.ItemIndex of
    0: begin
         FValues[0] := StrToFloat(edP1.Text);
         CalcDCRes;
     end;
    1: begin
         FValues[0] := StrToFloat(edP1.Text)*1.4142; // sine RMS to amplitude
         CalcDCRes;
     end;
    2: begin
         FValues[0] := StrToFloat(edP1.Text)*1.4142; // sine RMS to amplitude
         CalcACCap;
     end;
  end;
end;

procedure TfmLEDCalc.DoFieldChange(Sender: TObject);
begin
  inherited;
  DoFieldChanged;
end;

class function TfmLEDCalc.CalcFriendlyName: string;
begin
  Result := 'LED Series resistor';
end;

class function TfmLEDCalc.RunKey: string;
begin
  Result := 'ledres';
end;

class procedure TfmLEDCalc.InvokeCalc;
begin
  Application.CreateForm(TfmLEDCalc, fmLEDCalc);
  inherited;
end;

procedure TfmLEDCalc.imgResClick(Sender: TObject);
var tmp: single;
begin
  Self.LockFields;
  try
    FValues[4] := GetE24(FValues[4]);
    imgRes.Visible := false;
    tmp := (FValues[0]-(FValues[1]*FValues[3]))/FValues[4]*1e3;
    edP3.Text := Format('%.2f', [tmp]);
  finally UnlockFields; end;
  DoFieldChanged;
end;

procedure TfmLEDCalc.cbLEDBaseChange(Sender: TObject);
var tmp: single;
begin
  inherited;
  sbDelete.Enabled := cbLEDBase.ItemIndex>-1;
  if (cbLEDBase.ItemIndex = -1) then exit;
  
  tmp := readFloatAttr(FBase.DocumentRoot[cbLEDBase.ItemIndex], 'vf');
  edP2.Text := Format('%f', [tmp]);
  tmp := readFloatAttr(FBase.DocumentRoot[cbLEDBase.ItemIndex], 'if');
  edP3.Text := Format('%f', [tmp]);
end;

procedure TfmLEDCalc.BeforeDestruction;
begin
  FBase.Save(AppDir+'Data\led.xml');
  FBase.Free;
  inherited;
end;

procedure TfmLEDCalc.sbAddClick(Sender: TObject);
var s: string;
    i,idx: integer;
    node: TPXMLNode;
begin
  s := cbLEDBase.Text;
  if InputQuery(GetLocaleString('addcap'), GetLocaleString('addtext'), s) then begin
    node := nil;
    // Find by text
    for i := 0 to FBase.DocumentRoot.ChildCount-1 do
      if WideSameText(FBase.DocumentRoot[i].getAttribute('name'), s) then begin
        node := FBase.DocumentRoot[i];
        break;
      end;
    if (node=nil) then begin
      node := TPXMLNode.Create(FBase.DocumentRoot);
      node.Name := 'entry';
      node.setAttribute('name', s);
    end;
    node.setAttributeF('vf', checkField(edP2));
    node.setAttributeF('if', checkField(edP3));
    idx := node.Index;

    Self.ReloadList;
    cbLEDBase.ItemIndex := idx;
  end;
end;

procedure TfmLEDCalc.sbDeleteClick(Sender: TObject);
begin
  inherited;
  if cbLEDBase.ItemIndex=-1 then exit;
  if MessageBox(GetLocaleString('deltext'),GetLocalestring('delcap'), MB_ICONQUESTION or MB_YESNO)<>IDYES then exit;
end;

procedure TfmLEDCalc.edP3Change(Sender: TObject);
begin
  inherited;
  if (edP2.Focused or edP3.Focused) then cbLEDBase.ItemIndex := -1;
  DoFieldChange(nil);
end;

procedure TfmLEDCalc.ReloadList;
var i: integer;
begin
  cbLEDBase.Clear;
  for i := 0 to FBase.DocumentRoot.ChildCount-1 do
    cbLEDBase.AddItem(FBase.DocumentRoot[i].getAttribute('name'), nil);
end;

procedure TfmLEDCalc.cbVccSourceChange(Sender: TObject);
begin
  inherited;
  laRCap.Visible := (cbVccSource.ItemIndex=2);
  laR2.Visible := (cbVccSource.ItemIndex=2);

  case cbVccSource.ItemIndex of
    0: begin
         SetLabels('P1', getString('#global\vcc'), getString('#global\volt'));
         SetLabels('Result', getLocaleString('result'), '');
         LoadPictureFromRes(imgMain.Picture, 'LED_RES');
     end;
    1: begin
         SetLabels('P1', getString('#global\vcc')+' (RMS)', getString('#global\volt'));
         SetLabels('Result', getLocaleString('result'), '');
         LoadPictureFromRes(imgMain.Picture, 'LED_RES_AC');
      end;
    2: begin
         SetLabels('P1', getString('#global\vcc')+' (RMS)', getString('#global\volt'));
         SetLabels('Result', getLocaleString('resultcap'), '');
         LoadPictureFromRes(imgMain.Picture, 'LED_CAP');
      end;
  end;
  Self.DoFieldChange(Self);
end;

procedure TfmLEDCalc.CalcACCap;
begin
  //FValues[4] := (FValues[0]-(FValues[1]*FValues[3]))/(FValues[2]*0.001);
  FValues[4] := (4.45*FValues[2])/(FValues[0]-FValues[1]*FValues[3]);

  edResult.Text := FormatValueC(FValues[4]*1e-6);
  imgRes.Visible := false;//IsInE24(FValues[4]); //abs(FValues[4]-GetE24(FValues[4]))>0.1;
end;

procedure TfmLEDCalc.CalcDCRes;
begin
  if (FValues[0]-(FValues[1]*FValues[3]) < 0) then begin
    edResult.Text := getLocaleString('lowvolt');
    exit;
  end;

  FValues[4] := (FValues[0]-(FValues[1]*FValues[3]))/(FValues[2]*0.001);
  FValues[5] := (FValues[2]*0.001)*(FValues[0]-(FValues[1]*FValues[3]));

  edResult.Text := Format('%.1f Ohm, %sW', [FValues[4], formatValue(FValues[5])]);
  imgRes.Visible := IsInE24(FValues[4]); //abs(FValues[4]-GetE24(FValues[4]))>0.1;
end;

initialization
  RegisterCalc(TfmLEDCalc);

end.
